/**
 * Software Analysis and Verification 
 * @project: Verify Pattern Matching with Guards in Scala
 * @authors: Mirco Dotta and Philippe Suter
 */


package ch.epfl.lara.patterns.analyze;

import ch.epfl.lara.patterns.ast._

import scala.collection.immutable.Map


class HierarchyHolder(hierarchy: Map[String, HierarchyKlass]) extends FormulaWrapper with Constants{

  /**
   * <p>
   * 	Obtain the hierarchy definition. 
   * <p>
   * @return : The data structure Map[String,HierarchyKlass] that contains the hierarchy definition.
   *	       The first entry is the name of the class (that give O(1) complexity when we lookup for a class 
   *	       whose name is known) and the second is the instance that store all information about the class
   *	       definition.
   */
  def getHierarchy = hierarchy
  
  /**
   * <p>
   *	Get the root class of the hierarchy.
   * </p>
   * @return : The root class definition
   */
  def getKlassRoot(klass: String): HierarchyKlass = {
    val k = lookupKlass(klass) 
    k.parent match {
      case None => k
      case Some(parent) => getKlassRoot(parent)
    }
   }
   
   def getKlassSelfAndDescendants(klass: String): List[HierarchyKlass] = 
     lookupKlass(klass) :: getKlassDescendants(klass)
   
   def getKlassDescendants(klass: String): List[HierarchyKlass] = 
     (hierarchy.values.toList) filter (k => isAncestorOf(k.name, klass))
   
   def getKlassChildren(klass: String): List[HierarchyKlass] =
     (hierarchy.values.toList) filter (k2 => k2.parent equals Some(klass))
   
     
   def isAncestorOf(descendant: String, ancestor: String): Boolean = {
     !(descendant equals ancestor) && 
     (lookupKlass(descendant).parent match {
       case None => false
       case Some(parent) => (parent equals ancestor) || isAncestorOf(parent, ancestor) 
     })
   }
   /**
    * <p>
    *    Returns the list of classes in the hierarchy which have no parent
    * </p>
    */
   def getRootKlasses: List[HierarchyKlass] =
      hierarchy.values.toList filter (k => k.parent equals None)
  
  /**
   * <p>
   * 	Used to look up for any class of the hierarchy.       
   * </p>
   * @param klass: The name of the class (or object) that has to be looked up
   * @return     : The instance that store all informations about the looked up class
   * @throw      : An Exception if the looked up class doesn't belogn to the hierarchy                   
   */
  def lookupKlass(klass: String): HierarchyKlass = (hierarchy get klass) match {
    case None => throw new Exception("class " + klass + " has not been defined in the hierarchy")
    case Some(klassDef) => klassDef 
  }
   
   
  /**
   * <p>
   * 	Get all superclasses of the class that is passed in argument.
   * </p>
   * @param klass: The name of a class
   * @return     : The List of all super classes (Nil if the class passed as argument is the root class)
   */
  def getKlassParentList(klass: String): List[HierarchyKlass] = lookupKlass(klass) match {
     case HierarchyKlass(_,_,_,_,None,_) => Nil
     case HierarchyKlass(_,_,_,_,Some(parent),_) => lookupKlass(parent) :: getKlassParentList(parent)
  } 
    
	  
}
